using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace CppCliWrapper
{
    public static class TypeConverter
    {
        public class TypeTranslation
        {
            private readonly Type _managedType;
            private readonly string _nativeType;
            private readonly bool _marshalingRequired;

            public TypeTranslation(Type managedType, string nativeType)
            {
                _managedType = managedType;
                _nativeType = nativeType;
            }

            public TypeTranslation(Type managedType, string nativeType, bool marshalingRequired)
            {
                _managedType = managedType;
                _nativeType = nativeType;
                _marshalingRequired = marshalingRequired;
            }

            public static TypeTranslation Create<TType>(string toType)
            {
                return new TypeTranslation(typeof(TType), toType);
            }

            public static TypeTranslation CreateRequiresMarshaling<TType>(string toType)
            {
                return new TypeTranslation(typeof(TType), toType, true);
            }

            public Type ManagedType
            {
                get { return _managedType; }
            }

            public string NativeType
            {
                get { return _nativeType; }
            }

            public bool MarshalingRequired
            {
                get { return _marshalingRequired; }
            }
        }

        static Dictionary<Type, TypeTranslation> _standardTranslations = new Dictionary<Type, TypeTranslation>();

        static TypeConverter()
        {
            List<TypeTranslation> translations = new List<TypeTranslation>();
            translations.Add(TypeTranslation.Create<int>("int"));
            translations.Add(TypeTranslation.Create<long>("__int64"));
            translations.Add(TypeTranslation.Create<short>("short"));
            translations.Add(TypeTranslation.Create<byte>("unsigned __int8"));
            translations.Add(TypeTranslation.Create<float>("float"));
            translations.Add(TypeTranslation.Create<double>("double"));
            translations.Add(TypeTranslation.Create<char>("wchar_t"));
            translations.Add(TypeTranslation.CreateRequiresMarshaling<string>("wchar_t*"));//std::wstring?
            translations.Add(TypeTranslation.Create<bool>("bool"));
            translations.Add(new TypeTranslation(typeof(void), "void"));
            //TODO: More standard translations (unsigned, etc.)

            foreach (TypeTranslation translation in translations)
                _standardTranslations.Add(translation.ManagedType, translation);
        }

        public static TypeTranslation TranslateParameterType(Type parameterType)
        {
            TypeTranslation translation;
            if (_standardTranslations.TryGetValue(parameterType, out translation))
                return translation;

            if (parameterType.IsArray)
            {
                if (_standardTranslations.TryGetValue(parameterType.GetElementType(), out translation))
                {
                    string nativeType = translation.NativeType;

                    for (int i = 0; i < parameterType.GetArrayRank(); ++i)
                        nativeType += "*";

                    return new TypeTranslation(parameterType, nativeType, true);
                }
            }

            //TODO: Try more things, like collections
            //TODO: Convert structures recursively?

            return new TypeTranslation(parameterType, Utils.GetCppCliTypeNameFor(parameterType), true);
        }
    }

    public static class Utils
    {
        public static string GetCppCliTypeNameFor(Type type)
        {
            return type.FullName.Replace(".", "::");
        }

        public static void GenerateStandardDllExportDefinitions(StringBuilder headerBuilder)
        {
            headerBuilder.AppendLine("#ifdef THISDLL_EXPORTS");
            headerBuilder.AppendLine("#define THISDLL_API __declspec(dllexport)");
            headerBuilder.AppendLine("#else");
            headerBuilder.AppendLine("#define THISDLL_API __declspec(dllimport)");
            headerBuilder.AppendLine("#endif");
        }

        public static string GetLocalTempNameFor(ParameterInfo parameter)
        {
            return "__Param_" + parameter.Name;
        }

        public static string GetCppCliVariableNameFor(Type type)
        {
            string bestEffort = GetCppCliTypeNameFor(type);
            if (!type.IsValueType)
                return bestEffort + "^";
            return bestEffort;
        }

        public static string GetILBridgeTypeNameFor(Type type)
        {
            return "ILBridge_" + type.FullName.Replace('.', '_');
        }

        public static string GetNativeExportedClassNameFor(Type type)
        {
            return "NativeExport_" + type.FullName.Replace('.', '_');
        }
    }
}